package com.example.threadcode.basicLeverThree.PC.v1;

/**
 * 这个应用创建创建一个Shared对象和两个线程,这两线程获取对象的引用的副本
 * 生产者调用setSharedChar()方法去保存字符数据
 * 消费者调用getSharedChar()方法去获取每个字符数据
 * writeable实例域属性追踪两个条件:
 * 1. 生产者等待消费者消费数据并且
 * 2. 消费者等待生产者生产新数据
 * 这就会促使生产者和消费者协同执行,下面举例说明如何协同执行
 * 1. 消费者执行getSharedChar()去获取一个字符
 * 2. 在同步方法区中,消费者调用wait(),因为writeable初始化为true,那么现在消费线程开始等待直到生产线程的通知
 * 3. 生产线程最终执行setSharedChar(ch)方法
 * 4. 当生产线程进入同步方法区中,发现writeable是true那么不会调用wait()
 * 5. 生产线程保存字符后,把writeable设置为false(这将会导致在下一次生产者保存字符的时候进入wait()),并且调用notify方法去唤醒消费者
 * 6. 生产线程退出setSharedChar(ch)同步方法区
 * 7. 消费者被唤醒并重新获得锁,把writeable设置为true(这将会导致在下一次消费者获取字符时候进入wait()),通知唤醒生产者并返回字符
 *
 * 出现的结果很奇怪,有先生产几个后消费几个,或先消费后生产的.这其实并不是因为没有加锁同步,这是因为在调用setSharedChar方法
 * 之后的system.out.println()方法没有加synchronized锁,getSharedChar后面的哪个输出也没有加锁
 * 可以通过包装这写方法调用在一个同步块中来输出正确的顺序.这个例子在V2中
 */
public class PC
{
   public static void main(String[] args)
   {
      Shared s = new Shared();
      new Producer(s).start();
      new Consumer(s).start();
   }
}

class Shared
{
   private char c;
   private volatile boolean writeable = true;

   synchronized void setSharedChar(char c)
   {
      while (!writeable)
         try
         {
            wait();
         }
         catch (InterruptedException ie) 
         {
         }
      this.c = c;
      writeable = false;
      notify();
   }

   synchronized char getSharedChar()
   {
      while (writeable)
         try
         {
            wait();
         }
         catch (InterruptedException ie) 
         {
         }
      writeable = true;
      notify();
      return c;
   }
}

class Producer extends Thread
{
   private final Shared s;

   Producer(Shared s)
   {
      this.s = s;
   }

   @Override
   public void run()
   {
      for (char ch = 'A'; ch <= 'Z'; ch++)
      {
         s.setSharedChar(ch);
//         注意这个输出语句没有在同步块中
         System.out.println(ch + " produced by producer.");
      }
   }
}
class Consumer extends Thread
{
   private final Shared s;

   Consumer(Shared s)
   {
      this.s = s;
   }

   @Override
   public void run()
   {
      char ch;
      do
      {
         ch = s.getSharedChar();
         //         注意这个输出语句没有在同步块中
         System.out.println(ch + " consumed by consumer.");
      }
      while (ch != 'Z');
   }
}